﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using Microsoft.Ccr.Core;
using Microsoft.Dss.Core.Attributes;
using Microsoft.Dss.ServiceModel.Dssp;
using Microsoft.Dss.ServiceModel.DsspServiceBase;
using W3C.Soap;
using System.Linq;
using TasksLibrary.Extensions;

namespace TasksLibrary
{
    public class TaskableDsspServiceBase : DsspServiceBase
    {
        Dictionary<int, Tasks.Task> mTasks = new Dictionary<int, Tasks.Task>();
        Port<DateTime> _timerPort = new Port<DateTime>();

        /// <summary>
        /// Service constructor
        /// </summary>
        public TaskableDsspServiceBase(DsspServiceCreationPort creationPort)
            : base(creationPort)
        {
        }

        #region ShowMessage
        public void ShowMessage(Exception ex)
        {
            ShowMessage(ex.Message);
        }

        public void ShowMessage(string text)
        {
            LogInfo(text);
            Console.WriteLine(text);
        }
        #endregion

        #region Log
        #region LogInfo
        public new void LogInfo(string text)
        {
            base.LogInfo(text);
            Console.WriteLine(text);
        }

        public new void LogInfo(Exception ex)
        {
            base.LogInfo(ex.ToString());
            Console.WriteLine(ex.ToString());
        }
        #endregion

        public new void LogError(string text)
        {
            base.LogError(text);
            Console.WriteLine(text);
        }
        #endregion

        public void InitQueue(params PortSet[] portSets)
        {
            try
            {
                System.Reflection.MethodInfo DsspOperationHandlerMethod = this.GetType().GetMethod("DsspOperationHandler");
                if (DsspOperationHandlerMethod != null)
                {
                    System.Reflection.MethodInfo ReceiveMethod = null;

                    var members = typeof(Arbiter).GetMethods();
                    foreach (var member in members)
                    {
                        if (member.Name == "Receive")
                        {
                            if (member.IsGenericMethod && member.GetParameters().Length == 3)
                            {
                                ReceiveMethod = member;
                            }
                        }
                    }

                    if (ReceiveMethod != null)
                        foreach (PortSet set in portSets)
                        {
                            try
                            {
                                Console.WriteLine("PortSet: " + set.ToString() + ", Mode: " + set.Mode.ToString());
                                int i = 0;
                                Microsoft.Ccr.Core.Arbiters.ReceiverTask[] branches = new Microsoft.Ccr.Core.Arbiters.ReceiverTask[set.Ports.Count];

                                foreach (var port in set.Ports)
                                {
                                    try
                                    {
                                        Type portType = port.GetType();
                                        var args = portType.GetGenericArguments();
                                        if (args.Length == 1)
                                        {
                                            var handlType2 = typeof(Handler<>).MakeGenericType(args[0]);
                                            var applyRaise2 = Delegate.CreateDelegate(handlType2, this, DsspOperationHandlerMethod);

                                            System.Reflection.MethodInfo genericMethod = ReceiveMethod.MakeGenericMethod(args[0]);

                                            object ret = genericMethod.Invoke(this, new object[] { true, port, applyRaise2 });
                                            branches[i++] = (Microsoft.Ccr.Core.Arbiters.ReceiverTask)ret;

                                            //Activate<Microsoft.Ccr.Core.ITask>((Microsoft.Ccr.Core.ITask)ret);
                                        }
                                    }
                                    catch (Exception ex)
                                    {
                                        ShowMessage("InitQueue: " + ex.Message);
                                    }
                                }

                                MainPortInterleave.CombineWith(
                                    new Interleave(
                                        new TeardownReceiverGroup(),
                                        new ExclusiveReceiverGroup(branches),
                                        new ConcurrentReceiverGroup()
                                    )
                                );
                            }
                            catch (Exception ex)
                            {
                                ShowMessage("InitQueue: " + ex.Message);
                            }
                        }
                }
            }
            catch (Exception ex)
            {
                ShowMessage("InitQueue: " + ex.Message);
            }
            _timerPort.Post(DateTime.Now);

            MainPortInterleave.CombineWith(
                new Interleave(
                    new TeardownReceiverGroup(),
                    new ExclusiveReceiverGroup(
                        Arbiter.Receive(true, _timerPort, TimerHandler)
                    ),
                    new ConcurrentReceiverGroup()
                )
            );
            //Activate(Arbiter.Receive(true, _timerPort, TimerHandler));

        }

        public void DsspOperationHandler(object Message)
        {
            HandleMessage(Message);
        }

        /// <summary>
        /// Timer Handler
        /// </summary>
        /// <param name="signal">Not used</param>
        void TimerHandler(DateTime signal)
        {
            int TimeLimit = 1000;
            try
            {
                var timenow = DateTime.Now;
                //foreach (var task in mTasks)
                for( int i=0; i < mTasks.Count;)
                {
                    var task = mTasks.ElementAt(i);
                    if (task.Value.CheckComplete())
                    {
                        if (!task.Value.IsPeriodicTask) mTasks.Remove(task.Key);
                        else i++;
                        task.Value.TryCloseByComplete();
                    }
                    else
                    {
                        if (task.Value.CheckTimeout(TimeLimit))
                        {
                            if (!task.Value.IsPeriodicTask) mTasks.Remove(task.Key);
                            else i++;
                            task.Value.TryCloseByTimeout(TimeLimit);
                        }
                        else i++;
                    }
                }
            }
            catch (Exception ex)
            {
                LogError(ex);
            }

            // Set the timer for the next tick
            Activate(
                Arbiter.Receive(false, TimeoutPort(100),
                    delegate(DateTime time)
                    {
                        _timerPort.Post(time);
                    }
                )
            );
        }

        public bool NewTask(Tasks.Task task)
        {
            try
            {
                if (task == null) return false;
                if (IsHaveTask(task.Id)) return false;
                if (GetTask(task.Name) != null) return false;

                mTasks.Add(task.Id, task);
            }
            catch (Exception ex)
            {
                ShowMessage("NewTask: " + ex.Message);
            }
            return false;
        }

        private void HandleMessage(object Message)
        {
            try
            {
                if (mTasks.Count == 0) return;
                if (Message == null) return;

                int TaskMessageId = 0;

                bool canWork = false;
                Type messageType = Message.GetType();
                Type objType = typeof(object);
                for (int i = 0; i < 5; i++)
                {
                    if (messageType.IsGenericType)
                    {
                        if (messageType.GetGenericTypeDefinition() == typeof(Update<,>))
                        {
                            canWork = true;
                            break;
                        }
                    }
                    if (messageType.BaseType == objType) break;
                    messageType = messageType.BaseType;
                }

                if (canWork)
                {
                    var PropertiesMessage = Message.GetType().GetProperties();
                    TaskMessageRequest MessageBody = null;
                    foreach (var property in PropertiesMessage)
                    {
                        if (property.Name == "Body" && Inc.Types.CheckBaseType(property.PropertyType, typeof(TaskMessageRequest)))
                        {
                            MessageBody = (TaskMessageRequest)property.GetValue(Message, null);
                            TaskMessageId = MessageBody.TaskId;
                            break;
                        }
                    }
                }

                foreach (var task in mTasks)
                {
                    if (task.Key == TaskMessageId || TaskMessageId == 0)
                        task.Value.TryMessageComplete(Message);
                }
            }
            catch (Exception ex)
            {
                ShowMessage("HandleMessage: " + ex.Message);
            }

        }

        public bool IsHaveTask(int TaskId)
        {
            try
            {
                if (mTasks.ContainsKey(TaskId)) return true;
            }
            catch (Exception ex)
            {
                ShowMessage("IsHaveTask(Id=" + TaskId.ToString() + "): " + ex.Message);
            }
            return false;
        }

        public Tasks.Task GetTask(int TaskId)
        {
            try
            {
                if (IsHaveTask(TaskId)) return mTasks[TaskId];
            }
            catch (Exception ex)
            {
                ShowMessage("GetTask(Id=" + TaskId.ToString() + "): " + ex.Message);
            }
            return null;
        }

        public Tasks.Task GetTask(string TaskName)
        {
            try
            {
                foreach (var Task in mTasks)
                    if (Task.Value.Name == TaskName)
                        return Task.Value;
            }
            catch (Exception ex)
            {
                ShowMessage("GetTask(Name=" + TaskName + "): " + ex.Message);
            }
            return null;
        }
    }
}
